package org.jeecg.modules.outOrder.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.constant.WarehouseConstant;
import org.jeecg.modules.basicSkuInfo.entity.BasicSkuInfo;
import org.jeecg.modules.basicSkuInfo.service.IBasicSkuInfoService;
import org.jeecg.modules.inventory.entity.InventoryInfo;
import org.jeecg.modules.inventory.service.IInventoryInfoService;
import org.jeecg.modules.outOrder.entity.OutOrder;
import org.jeecg.modules.outOrder.entity.OutOrderDetail;
import org.jeecg.modules.outOrder.mapper.OutOrderDetailMapper;
import org.jeecg.modules.outOrder.mapper.OutOrderMapper;
import org.jeecg.modules.outOrder.service.IOutOrderDetailService;
import org.jeecg.modules.outOrder.service.IOutOrderService;
import org.jeecg.modules.outOrderTaskMain.entity.OutOrderTaskDetail;
import org.jeecg.modules.outOrderTaskMain.entity.OutOrderTaskMain;
import org.jeecg.modules.outOrderTaskMain.service.IOutOrderTaskDetailService;
import org.jeecg.modules.outOrderTaskMain.service.IOutOrderTaskMainService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 出库单
 * @Author: jeecg-boot
 * @Date: 2023-12-04
 * @Version: V1.0
 */
@Service
public class OutOrderServiceImpl extends ServiceImpl<OutOrderMapper, OutOrder> implements IOutOrderService {

    @Resource
    private OutOrderMapper outOrderMapper;
    @Resource
    private OutOrderDetailMapper outOrderDetailMapper;
    @Autowired
    private IBasicSkuInfoService basicSkuInfoService;
    @Autowired
    private IOutOrderDetailService outOrderDetailService;
    @Autowired
    private IOutOrderTaskMainService outOrderTaskMainService;
    @Autowired
    private IOutOrderTaskDetailService outOrderTaskDetailService;
    @Autowired
    private IInventoryInfoService inventoryInfoService;

    @Override
    public void saveWarehousingOut(OutOrder outOrder, List<OutOrderDetail> outOrderDetailList) {
        // 生成订单号
        if (StringUtils.isBlank(outOrder.getOutOrderCode())) {
            outOrder.setOutOrderCode(DateUtils.getToday() + "-" + System.currentTimeMillis());
        }

        Integer skuType = outOrder.getGoodsType();
        // 校验输入的品番是否存在
        for (OutOrderDetail outOrderDetail : outOrderDetailList) {
            QueryWrapper<BasicSkuInfo> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("sku_type", skuType);
            queryWrapper.eq("sku_code", outOrderDetail.getSkuCode());

            if (Objects.equals(skuType, WarehouseConstant.SkuTypeEnum.PT.getCode())) {
                Assert.isTrue(StringUtils.isNotBlank(outOrderDetail.getCustomerName()), "请输入客户名称！");
                Assert.isTrue(StringUtils.isNotBlank(outOrderDetail.getCustomerSku()), "请输入客户品番！");
                queryWrapper.eq("customer_name", outOrderDetail.getCustomerName());
                queryWrapper.eq("customer_sku", outOrderDetail.getCustomerSku());
                int count = basicSkuInfoService.count(queryWrapper);
                Assert.isFalse(count == 0, "想要出库的品番编码【%s】、客户名称【%s】、客户品番【%s】不存在！", outOrderDetail.getSkuCode(), outOrderDetail.getCustomerName(), outOrderDetail.getCustomerSku());
            } else if (Objects.equals(skuType, WarehouseConstant.SkuTypeEnum.SF.getCode())) {
                int count = basicSkuInfoService.count(queryWrapper);
                Assert.isFalse(count == 0, "想要出库的品番编码【%s】不存在！", outOrderDetail.getSkuCode());
            }
        }
        // 保存入库单
        if (null == outOrder.getId()) {
            this.saveMain(outOrder, outOrderDetailList);
        } else {
            this.updateMain(outOrder, outOrderDetailList);
        }
    }

    @Override
    public void saveMain(OutOrder outOrder, List<OutOrderDetail> outOrderDetailList) {
        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode());
        outOrderMapper.insert(outOrder);
        if (CollectionUtils.isNotEmpty(outOrderDetailList)) {
            for (OutOrderDetail outOrderDetail : outOrderDetailList) {
                outOrderDetail.setOutOrderId(outOrder.getId());
                outOrderDetail.setStatus(CommonConstant.STATUS_1);
            }
            outOrderDetailService.saveBatch(outOrderDetailList);
        }
    }

    @Override
    public void updateMain(OutOrder outOrder, List<OutOrderDetail> outOrderDetailList) {
        outOrderMapper.updateById(outOrder);
        outOrderDetailMapper.deleteByMainId(outOrder.getId());
        if (CollectionUtils.isNotEmpty(outOrderDetailList)) {
            for (OutOrderDetail outOrderDetail : outOrderDetailList) {
                outOrderDetail.setOutOrderId(outOrder.getId());
            }
            outOrderDetailService.saveBatch(outOrderDetailList);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void releaseWarehousingOut(String id) {
        OutOrder outOrder = outOrderMapper.selectById(id);
        Assert.notNull(outOrder, "出库单数据异常，请联系管理员！");
        Assert.isTrue(WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode().equals(outOrder.getStatus()),
                "出库单当前状态：【" + WarehouseConstant.OutWarehouseEnum.getDefineByCode(outOrder.getStatus()) + "】,不支持发布！");

        // 更新出库单状态
        outOrder.setReleaseTime(new Date());
        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode());
        outOrderMapper.updateById(outOrder);

        // 生成出库任务
        this.createEnterTask(outOrder);
    }

    private void createEnterTask(OutOrder outOrder) {
        List<OutOrderDetail> outOrderDetailList = outOrderDetailService.list(new LambdaQueryWrapper<OutOrderDetail>().eq(OutOrderDetail::getOutOrderId, outOrder.getId()));
        Assert.notEmpty(outOrderDetailList, "出库单详情数据异常，请联系管理员！");

        // 获取本次要出库的所有品番对应的库存信息
        Set<String> skuCodeList = outOrderDetailList.stream().map(OutOrderDetail::getSkuCode).collect(Collectors.toSet());
        List<InventoryInfo> inventoryInfoList = inventoryInfoService.list(new LambdaQueryWrapper<InventoryInfo>()
                .in(InventoryInfo::getWarehouseCode, Arrays.asList(WarehouseConstant.WarehouseTypeEnum.AGV.getCode(), WarehouseConstant.WarehouseTypeEnum.MULTILAYER.getCode()))
                .in(InventoryInfo::getSkuCode, skuCodeList)
                .gt(InventoryInfo::getAvailableBoxQuantity, 0));
        Assert.notEmpty(inventoryInfoList, "本次输入的所有品番均没有剩余库存，请检查后重新发布！");

        List<OutOrderTaskDetail> agvTaskDetailList = new ArrayList<>(); // agv区的任务明细
        List<OutOrderTaskDetail> gwkTaskDetailList = new ArrayList<>(); // 高位库区的任务明细
        // 求出每个品番对应的出库总数
        Map<String, Integer> wantOutMap = outOrderDetailList.stream().collect(Collectors.groupingBy(OutOrderDetail::getSkuCode, Collectors.summingInt(OutOrderDetail::getBoxNumber)));
        for (String skuCode : wantOutMap.keySet()) {
            BasicSkuInfo skuInfo = basicSkuInfoService.list(new LambdaQueryWrapper<BasicSkuInfo>().eq(BasicSkuInfo::getSkuCode, skuCode)).get(0);

            // 想要出库数量
            Integer wantOutNumber = wantOutMap.get(skuCode);

            // 从全部品番的库存集合中过滤出当前循环中品番的库存集合
            List<InventoryInfo> skuIncentoryList = inventoryInfoList.stream()
                    .filter(data -> skuCode.equals(data.getSkuCode()))
                    .sorted(Comparator.comparing(InventoryInfo::getEnterDate))
                    .collect(Collectors.toList());
            Assert.notEmpty(skuIncentoryList, "品番编码【%s】已无剩余库存，无法出库", skuCode);
            // 现在仓库中有的箱数
            int haveNum = skuIncentoryList.stream().mapToInt(InventoryInfo::getAvailableBoxQuantity).sum();
            Assert.isFalse(wantOutNumber > haveNum, "品番编码【%s】想要出库数量：【%s】大于剩余库存数量：【%s】，库存不足无法出库！", skuCode, wantOutNumber, haveNum);

            for (InventoryInfo inventoryInfo : skuIncentoryList) {
                if (wantOutNumber == 0) {
                    continue;
                }

                Integer availableBoxQuantity = inventoryInfo.getAvailableBoxQuantity(); // 可用库存
                // 封装任务详情 一会儿根据品番编码决定放入agvTaskDetailList还是gwkTaskDetailList
                OutOrderTaskDetail outOrderTaskDetail = new OutOrderTaskDetail();
                outOrderTaskDetail.setShelfCode(inventoryInfo.getShelfCode());
                outOrderTaskDetail.setCompartmentCode(inventoryInfo.getCompartmentCode());
                outOrderTaskDetail.setSkuCode(skuCode);
                outOrderTaskDetail.setStatus(WarehouseConstant.OutWarehouseDetailEnum.OUT_NOT.getCode());
                if (Objects.equals(skuInfo.getWarehouseCode(), WarehouseConstant.WarehouseTypeEnum.MULTILAYER.getCode())) {
                    Assert.isTrue(StringUtils.isNotBlank(inventoryInfo.getStackCode()), "缺少货垛码！");
                    outOrderTaskDetail.setStackCode(inventoryInfo.getStackCode());
                }
                if (wantOutNumber > availableBoxQuantity) {
                    // 不够出
                    wantOutNumber -= availableBoxQuantity; // 想要出库箱数=想要出库箱数-可用库存箱数
                    outOrderTaskDetail.setNeedBoxQuantity(availableBoxQuantity); // 对应这条库存任务详情的出库数量是剩余数量
                    outOrderTaskDetail.setEndBoxQuantity(0);
                    outOrderTaskDetail.setEndGoodsQuantity(0);
                    inventoryInfo.setAvailableBoxQuantity(0);
                    inventoryInfo.setAvailableGoodsQuantity(0);
                } else {
                    // 够出
                    outOrderTaskDetail.setNeedBoxQuantity(wantOutNumber); // 对应这条库存任务详情的出库数量是剩余数量
                    outOrderTaskDetail.setEndBoxQuantity(0);
                    outOrderTaskDetail.setEndGoodsQuantity(0);
                    inventoryInfo.setAvailableBoxQuantity(availableBoxQuantity - wantOutNumber);
                    inventoryInfo.setAvailableGoodsQuantity(inventoryInfo.getAvailableGoodsQuantity() - (wantOutNumber * skuInfo.getBoxCapacity()));
                    wantOutNumber = 0; // 想要出库数量=0
                }

                // 判断这个出库任务属于agv区还是高位库区
                if (Objects.equals(skuInfo.getWarehouseCode(), WarehouseConstant.WarehouseTypeEnum.AGV.getCode())) {
                    agvTaskDetailList.add(outOrderTaskDetail);
                } else if (Objects.equals(skuInfo.getWarehouseCode(), WarehouseConstant.WarehouseTypeEnum.MULTILAYER.getCode())) {
                    gwkTaskDetailList.add(outOrderTaskDetail);
                }
            }
            // 当前循环中品番的出库任务详情已经收集完了，更新数据库中的剩余可用库存
            inventoryInfoService.updateBatchById(skuIncentoryList);
        }

        // 创建任务
        if (CollectionUtils.isNotEmpty(agvTaskDetailList)) {
            OutOrderTaskMain outOrderTaskMain = new OutOrderTaskMain();
            outOrderTaskMain.setOutOrderId(outOrder.getId());
            outOrderTaskMain.setExecuteStaff(WarehouseConstant.OutWarehouseTaskStaffEnum.TALLY.getCode());
            outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode());
            outOrderTaskMainService.save(outOrderTaskMain);

            agvTaskDetailList.forEach(data -> data.setOutOrderTaskId(outOrderTaskMain.getId()));
            outOrderTaskDetailService.saveBatch(agvTaskDetailList);
        }
        if (CollectionUtils.isNotEmpty(gwkTaskDetailList)) {
            OutOrderTaskMain outOrderTaskMain = new OutOrderTaskMain();
            outOrderTaskMain.setOutOrderId(outOrder.getId());
            outOrderTaskMain.setExecuteStaff(WarehouseConstant.OutWarehouseTaskStaffEnum.DRIVER.getCode());
            outOrderTaskMain.setStatus(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode());
            outOrderTaskMainService.save(outOrderTaskMain);

            gwkTaskDetailList.forEach(data -> data.setOutOrderTaskId(outOrderTaskMain.getId()));
            outOrderTaskDetailService.saveBatch(gwkTaskDetailList);
        }
        // 更新出库单
        if (CollectionUtils.isNotEmpty(agvTaskDetailList) && CollectionUtils.isNotEmpty(gwkTaskDetailList)) {
            outOrder.setExecuteStaff(WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY_DRIVER.getCode());
        } else if (CollectionUtils.isEmpty(agvTaskDetailList) && CollectionUtils.isNotEmpty(gwkTaskDetailList)) {
            outOrder.setExecuteStaff(WarehouseConstant.EnterWarehouseTaskStaffEnum.DRIVER.getCode());
        } else if (CollectionUtils.isNotEmpty(agvTaskDetailList) && CollectionUtils.isEmpty(gwkTaskDetailList)) {
            outOrder.setExecuteStaff(WarehouseConstant.EnterWarehouseTaskStaffEnum.TALLY.getCode());
        }
        outOrder.setStatus(WarehouseConstant.OutWarehouseEnum.RELEASE_END.getCode());
        outOrder.setReleaseTime(new Date());
        outOrderMapper.updateById(outOrder);
    }

    @Override
    @Transactional
    public void delMain(String id) {
        OutOrder inOrder = outOrderMapper.selectById(id);
        Assert.notNull(inOrder, "不存在的出库单无法进行删除！");
        Assert.isTrue(inOrder.getStatus().equals(WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode()), "出库单只有在发布前才可删除，当前状态不支持删除！");
        outOrderDetailMapper.deleteByMainId(id);
        outOrderMapper.deleteById(id);
    }

    @Override
    @Transactional
    public void delBatchMain(Collection<? extends Serializable> idList) {
        List<OutOrder> inOrderList = outOrderMapper.selectList(new QueryWrapper<OutOrder>()
                .eq("id", idList)
                .eq("status", WarehouseConstant.OutWarehouseEnum.CREATE_END.getCode()));
        Assert.isTrue(idList.size() == inOrderList.size(), "出库单只有在发布前才可删除，当前状态不支持删除！");


        outOrderMapper.update(
                new OutOrder().setStatus(WarehouseConstant.OutWarehouseEnum.DELETE.getCode()),
                new QueryWrapper<OutOrder>().in("id", idList));

        outOrderDetailMapper.update(
                new OutOrderDetail().setStatus(WarehouseConstant.OutWarehouseDetailEnum.DELETE.getCode()),
                new QueryWrapper<OutOrderDetail>().in("out_order_id", idList));
    }

}
